#include "Common.h"

initSing(WorldSocket);

DataBuffer::DataBuffer()
{
}

void DataBuffer::Push(ui8* src, ui32 len)
{
	m_access.Acquire();
	for(ui32 i = 0; i < len; ++i)
		_data.push_back(*(src + i));
	m_access.Release();
}

void DataBuffer::Pop(ui8* dest, ui32 len)
{
	for(ui32 i = 0; i < len; ++i)
	{
		*(dest + i) = *_data.begin();
		_data.erase(_data.begin());
	}
}

void DataBuffer::BlockAccess()
{
	m_access.Acquire();
}

void DataBuffer::AllowAccess()
{
	m_access.Release();
}

void WorldSocket::Loop() // virtual void CThread::Loop() implementation
{
	WSADATA wsd;
	WSAStartup(MAKEWORD(2, 0), &wsd); // Try starting winsock (doenst matter if its already started, just to be sure)
	m_listensocket = socket(AF_INET, SOCK_STREAM, 0); // Create our socket that listens for new clients
	SOCKADDR_IN sin;
	memset(&sin, NULL, sizeof(SOCKADDR_IN));

	// Bind on the port specified in the worldconfig
	// 127.0.0.1:8129 <-- We take the stuff after the : as port
	sin.sin_port = htons(sLogonClient->GetListenPort()); 
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_family = AF_INET;
	// Try binding on the specified port (if it fails, nothing will work)
	if(::bind(m_listensocket, (const sockaddr*)&sin, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
	{
		Log->Error("WorldSocket", "Bind unsuccessfull on port %u (Helpcode: 10)", sLogonClient->GetListenPort());
		return;
	}
	listen(m_listensocket, 10); // listen for new clients (that actually wont fail if we are bound)
	while(ThreadState)
	{
		FD_ZERO(&fset); // Clear our readset
		FD_SET(m_listensocket, &fset); // Put the listensocket into it to make sure, we find the new clients
		if(m_sessions.size()) // Dont waste time if we dont have any clients
		{
			set<Session*>::iterator itr = m_sessions.begin();
			set<Session*>::iterator end = m_sessions.end();

			for( ; itr != end; ++itr)
				FD_SET((*itr)->m_socket, &fset);
		}
		select(0, &fset, NULL, NULL, NULL); // update the readset
		if(FD_ISSET(m_listensocket, &fset)) // the listensocket is readable --> new client is waiting
		{
			Session* sess = new Session(); // start a new session
			sess->m_socket = accept(m_listensocket, NULL, NULL); // accept the client
			sess->ThreadState = 1;
			ThreadPool.ExecuteTask(sess); // execute the buffer-cleaner
			// add the client (needs no Mutex because this will be the only thread
			// accessing the clients
			m_sessions.insert(sess);  // Yes! a client! We better try to remember it, so well put it in the set
			sess->OnConnect(); // Handle connecting stuff
		}

		if(m_sessions.size()) // Again dont waste time if there is nothing to do
		{
			set<Session*>::iterator itr = m_sessions.begin();
			set<Session*>::iterator end = m_sessions.end();

			for( ; itr != m_sessions.end() && m_sessions.size(); ++itr)
			{
				if(FD_ISSET((*itr)->m_socket, &fset)) // Do we have data to be read?
				{
					char* buf = new char[65535];
					// Better make sure, that no other threads are sending data
					// while we are reading the sent data :D
					(*itr)->m_socketlock.Acquire();
					int x = recv((*itr)->m_socket, buf, 65535, 0);
					(*itr)->m_socketlock.Release();
					if(x == SOCKET_ERROR || x == 0) // client is no longer reachable
					{
						(*itr)->Disconnect(); // We need to disconnect first because after the session wont exist anymore!
						(*itr)->ThreadState = 0; // It is some confusion about this...
						m_sessions.erase(itr); // take out the session
						delete [] buf; // free the data
					}
					else // We have data!! :O:O
					{
						(*itr)->m_readbuffer.Push((ui8*)buf, x); // Were lazy, let the session take care of it ;)
						delete [] buf;
					}
				}
			}
		}
	}
}

Session* WorldSocket::GetSessionByRequest(ui32 id)
{
	m_sessionlock.Acquire();
	set<Session*>::iterator itr = m_sessions.begin();
	set<Session*>::iterator end = m_sessions.end();

	for( ; itr != end; ++itr)
	{
		if((*itr)->m_request == id)
			return (*itr);
	}
	return NULL;
}